//
//  HSMediator.m
//  ObjcDemo
//
//  Created by fan on 2021/10/15.
//

#import "HSMediator.h"
#import <objc/runtime.h>
#import <CoreGraphics/CoreGraphics.h>

@class HSTestClass;

@interface HSMediator ()

@property (nonatomic, strong) NSMutableDictionary *cachedTarget;

@end

@implementation HSMediator

+ (instancetype)sharedInstance {
    static HSMediator *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[HSMediator alloc] init];
        [mediator cachedTarget];
    });
    return mediator;
}

/*
 scheme://[target]/[action]?[params]
 
 url sample:
 aaa://targetA/actionB?id=1234
 */
- (id)dispatchWithUrl:(NSURL *)url{
    if (url == nil||![url isKindOfClass:[NSURL class]]) {
        return nil;
    }
    
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString];
    // 遍历所有参数
    [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.value&&obj.name) {
            [params setObject:obj.value forKey:obj.name];
        }
    }];
    
    NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
    
    if ([actionName hasPrefix:@"native"]) {
        return @(NO);
    }
    
    HSTargetAction *tarAct = HSTargetAction.cls(url.host).mtd(actionName).params(HSParameters.parameters(params));
    return [self dispatch:tarAct];
}

- (id)dispatch:(HSTargetAction *)targetAction{
    return [self dispatch:targetAction shouldCacheTarget:NO];
}

- (id)dispatch:(HSTargetAction *)targetAction shouldCacheTarget:(BOOL)shouldCacheTarget{
    //从缓存取
    NSObject *target = [self safeFetchCachedTarget:targetAction.targetClass];
    if (target == nil) {
        Class targetClass = NSClassFromString(targetAction.targetClass);
        target = [[targetClass alloc] init];
    }
    
    SEL method = NSSelectorFromString(targetAction.actionMethod);
    
    if (target == nil) {
        //没有可调用对象 做错误处理
        return [self noTargetActionResponseWith:targetAction];
    }
    
    if ([target respondsToSelector:method]) {
        if (shouldCacheTarget) {
            [self safeSetCachedTarget:target key:targetAction.targetClass];
        }
        return [self executeMethod:method target:target parameters:targetAction.parameters];
    }else{
        //调用对象没有实现方法 做错误处理
        return [self noTargetActionResponseWith:targetAction];
    }
}

// 核心方法调用
- (id)executeMethod:(SEL)action target:(NSObject *)target parameters:(HSParameters *)params {
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    const char* retType = [methodSig methodReturnType];

    if (strcmp(retType, @encode(void)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        return nil;
    }

    if (strcmp(retType, @encode(NSInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(BOOL)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        BOOL result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(CGFloat)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        CGFloat result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(NSUInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSUInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}

/// 没有可响应对象
- (id)noTargetActionResponseWith:(HSTargetAction *)targetAction{
    
    //判断字符串最后有没有 : 没有的话试一下添加: 重新dispatch
    if (![targetAction.actionMethod hasSuffix:@":"]) {
        targetAction.mtd([NSString stringWithFormat:@"%@:",targetAction.actionMethod]);
        return [self dispatch:targetAction];
    }else {
        NSString *str = [NSString stringWithFormat:@"No target-action Response target=[%@] action=[%@] params=[%@]", targetAction.targetClass, targetAction.actionMethod, targetAction.parameters.params];
        NSAssert(NO, str);
        return nil;
    }
}

#pragma mark - getters and setters
- (NSMutableDictionary *)cachedTarget
{
    if (_cachedTarget == nil) {
        _cachedTarget = [[NSMutableDictionary alloc] init];
    }
    return _cachedTarget;
}

- (NSObject *)safeFetchCachedTarget:(NSString *)key {
    @synchronized (self) {
        return self.cachedTarget[key];
    }
}

- (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key {
    @synchronized (self) {
        self.cachedTarget[key] = target;
    }
}


@end
